﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using System.Windows;
namespace FinosLibrary.Client {
    public class Logic {
        private static Dictionary<string, LogicPostArg> map = new Dictionary<string, LogicPostArg>();
        private static Dictionary<Type, object> autowire = new Dictionary<Type, object>();
        private static List<Logic> logics = new List<Logic>();
        public SynchronizationContext syn;
        public static void doLogic<T>(Package p, T connection) {
            if (!map.ContainsKey(p.head)) return;
            LogicPostArg Invoker = map[p.head];
            ParameterInfo[] ps = Invoker.mi.GetParameters();
            object[] arg = new object[ps.Length];
            arg[0] = connection;
            if (arg.Length > 1) {
                Type pt = ps[1].ParameterType;
                if (pt == typeof(string)) {
                    arg[1] = p.body;
                } else if (pt == typeof(Package)) {
                    arg[1] = p;
                } else {
                    arg[1] = typeof(Package).GetMethod("getBody").MakeGenericMethod(pt).Invoke(p, null);
                }
            }
            Invoker.arg = arg;
            if (Invoker.obj.syn != null) {
                Invoker.obj.syn.Post(_doLogic, Invoker);
            } else {
                _doLogic(Invoker);
            }
        }
        public static void doLogic(string fun, object b) {
            doLogic(new Package(fun, b));
        }
        public static void doLogic(string fun) {
            doLogic(new Package(fun));
        }
        public static void doLogic(Package p) {
            if (!map.ContainsKey(p.head)) return;
            LogicPostArg Invoker = map[p.head];
            ParameterInfo[] ps = Invoker.mi.GetParameters();
            object[] arg = new object[ps.Length];
            if (arg.Length > 0) {
                Type pt = ps[0].ParameterType;
                if (pt == typeof(string)) {
                    arg[0] = p.body;
                } else if (pt == typeof(Package)) {
                    arg[0] = p;
                } else {
                    arg[0] = typeof(Package).GetMethod("getBody").MakeGenericMethod(pt).Invoke(p, null);
                }
            }
            Invoker.arg = arg;
            if (Invoker.obj.syn != null) {
                Invoker.obj.syn.Post(_doLogic, Invoker);
            } else {
                _doLogic(Invoker);
            }
        }
        class LogicPostArg {
            public MethodInfo mi;
            public Logic obj;
            public object[] arg;
            private List<LogicPostArg> list;
            public void add(LogicPostArg more) {
                if (list == null) list = new List<LogicPostArg>();
                list.Add(more);
            }
            public void Invoke() {
                try {
                    if (list != null) {
                        foreach (LogicPostArg child in list) {
                            child.arg = arg;
                            child.Invoke();
                        }
                    }
                    mi.Invoke(obj, arg);
                } catch (Exception e) {
                    MessageBox.Show(e.InnerException != null ? e.InnerException.Message : e.Message, mi.Name, MessageBoxButton.OK);
                }
            }
        }
        public static void _doLogic(object arg) {
            LogicPostArg Arg = arg as LogicPostArg;
            Arg.Invoke();
        }
        public Logic() {
            if (GetType().GetCustomAttributes(typeof(MessageProcessAttribute), true).Length > 0) {
                foreach (MethodInfo mi in this.GetType().GetMethods()) {
                    MessageMethodAttribute ma = Attribute.GetCustomAttribute(mi, typeof(MessageMethodAttribute), true) as MessageMethodAttribute;
                    if (ma == null) continue;
                    if (map.ContainsKey(mi.Name))
                        map[mi.Name].add(new LogicPostArg() { mi = mi, obj = this });
                    else
                        map[mi.Name] = new LogicPostArg() { mi = mi, obj = this };
                }
                foreach (FieldInfo fi in this.GetType().GetFields()) {
                    AutoWireAttribute autoa = Attribute.GetCustomAttribute(fi, typeof(AutoWireAttribute)) as AutoWireAttribute;
                    if (autoa == null) continue;
                    Type ft = fi.FieldType;
                    if (!autowire.ContainsKey(ft)) autowire[ft] = Activator.CreateInstance(ft);
                    fi.SetValue(this, autowire[ft]);
                }
            }
            syn = SynchronizationContext.Current;
        }

        public static Logic GetInstance<T>() {
            Logic[] selectedLogics = new Logic[1];
            (from logic in logics where logic.GetType() == typeof(T) select logic).ToArray().CopyTo(selectedLogics,0);
            if (selectedLogics[0] == null) {
                selectedLogics[0] = typeof(T).GetConstructor(Type.EmptyTypes).Invoke(null) as Logic;
                logics.Add(selectedLogics[0]);
            }
            return selectedLogics[0];
        }
    }
}
